//*****************************************************************************
//
// autobaud.c - This file holds the implementation of the automatic baud rate
//              detection code.
//
// Copyright (c) 2006-2007 Luminary Micro, Inc.  All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws.  All rights are reserved.  Any use in violation
// of the foregoing restrictions may subject the user to criminal sanctions
// under applicable laws, as well as to civil liability for the breach of the
// terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 323 of the Stellaris boot loader.
//
//*****************************************************************************

#include "hw_memmap.h"
#include "hw_types.h"
#include "gpio.h"
#include "hw_gpio.h"
#include "hw_ssi.h"
#include "hw_uart.h"
#include "hw_nvic.h"
#include "boot_loader.h"

//*****************************************************************************
//
//! \defgroup autobaud_src AutoBaud API
//! This section describes the API necessary to interact with the auto-baud
//! rate detection code provided as part of the boot loader example.
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
//! This global holds the number of edges that have been stored in the global
//! buffer g_iTickBuffer.
//
//*****************************************************************************
volatile unsigned int g_uiTickIndex;

//*****************************************************************************
//
//! This define holds the maximum number of edges that are sent as a result of
//! a pattern of 2 bytes.
//
//*****************************************************************************
#define TICK_BUFFER_SIZE        (20)

//*****************************************************************************
//
//! This global buffer is used to hold the timing of each edge for baud rate
//! detection.
//
//*****************************************************************************
volatile int g_iTickBuffer[TICK_BUFFER_SIZE];

//*****************************************************************************
//
//! This define holds the multiplier for the pulse detection algorithm.  The
//! value is used to generate a fractional difference detection of
//! 1/PULSE_DETECTION_MULT.
//
//*****************************************************************************
#define PULSE_DETECTION_MULT    (3)

//*****************************************************************************
//
//! This define holds the minimum number of edges to successfully sync to
//! a pattern of 2 bytes.
//
//*****************************************************************************
#define MIN_EDGE_COUNT          (18)

//*****************************************************************************
//
//! Implements the auto-baud feature for the boot loader.
//!
//! \param pulRatio is the ratio of the processor's crystal frequency to the
//!     baud rate being used by the UART port for communications.  See
//!     UARTConfig() for a description of this value.
//!
//! This function attempts to synchronize to the updater program that is trying
//! to communicate with the boot loader.  The UART port is monitored for edges
//! using interrupts.  Once enough edges are detected, the boot loader
//! determines the ratio of baud rate and crystal frequency needed to program
//! the UART.
//!
//! \return A value of 0 indicates that this call successfully synchronized
//!     with the other device communicating over the UART.  A negative value
//!     indicates that this function did not successfully synchronize with the
//!     other UART device.
//
//*****************************************************************************
int
AutoBaud(unsigned int *pulRatio)
{
    int iPulse;
    int iValidPulses;
    int iTemp;
    int iTotal;

    iValidPulses = 0;
    iTotal = 0;
    g_uiTickIndex = 0;

    //
    // Set the pad(s) for standard push-pull operation.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_PUR) |= UART_RX;
    HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= UART_RX;

    //
    // Interrupt on both edges.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_IBE) = UART_RX;

    //
    // Clear out all of the gpio interrupts in this register.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX;

    //
    //  Enable the GPIO pin corresponding to the UART RX pin.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_IM) = UART_RX;

    //
    // Enable GPIOA Interrupt.
    //
    HWREG(NVIC_EN0) = 1;

    //
    // Wait for MIN_EDGE_COUNT to pass to collect enough edges.
    //
    while(g_uiTickIndex < MIN_EDGE_COUNT)
    {
    }

    //
    // Disable GPIOA Interrupt.
    //
    HWREG(NVIC_DIS0) = 1;

    //
    // Calculate the pulse width from the array of tick times.
    //
    for(iPulse = 0; iPulse < g_uiTickIndex - 1; iPulse++)
    {
        g_iTickBuffer[iPulse] = g_iTickBuffer[iPulse] -
            g_iTickBuffer[iPulse + 1];
    }

    //
    // This loops handles checking for consecutive pulses that have pulse
    // widths that are within an acceptable margin.
    //
    for(iPulse = 0; iPulse < g_uiTickIndex - 1; iPulse++)
    {
        //
        // Calculate the absolute difference between two consecutive pulses.
        //
        iTemp = g_iTickBuffer[iPulse] - g_iTickBuffer[iPulse + 1];
        if(iTemp < 0)
        {
            iTemp *= -1;
        }

        //
        // This pulse detection code uses the following algorithm:
        // If the following is true then we have consecutive acceptable pulses
        // abs(Pulse[n] - Pulse[n+1]) < Pulse[n+1]/PULSE_DETECTION_MULT
        // or
        // PULSE_DETECTION_MULT * abs(Pulse[n] - Pulse[n+1]) < Pulse[n+1]
        //
        if((iTemp * PULSE_DETECTION_MULT) < g_iTickBuffer[iPulse + 1])
        {
            iTotal += g_iTickBuffer[iPulse];
            iValidPulses++;
        }
        else
        {
            iValidPulses = 0;
            iTotal = 0;
        }

        //
        // Once we have 7 pulses calculate the ratio needed to program the
        // UART.
        //
        if(iValidPulses == 7)
        {
            //
            // Add in the last pulse and caculate the ratio.
            //
            iTotal+= g_iTickBuffer[iPulse];
            *pulRatio = iTotal >> 1;

            //
            // Wait for at least 2 UART clocks since we only wait for 18 of 20
            // that are coming from the host.  If we don't wait, we can turn
            // on the UART while the last two pulses come down.
            //
            while(iTotal--)
            {
            }

            //
            // Indicate a successful auto baud operation.
            //
            return(0);
        }
    }

    //
    // Automatic baud rate detection failed.
    //
    return(-1);
}

//*****************************************************************************
//
// This interrupt handler takes care of saving the timing of the pulses used
// to calculate what to set the dividers to.
//
//*****************************************************************************
void
GPIOIntHandler(void)
{
    //
    // Clear the GPIO interrupt source.
    //
    HWREG(GPIO_PORTA_BASE + GPIO_O_ICR) = UART_RX;

    //
    // While we still have space in our buffer, store the current system tick
    // count and return from interrupt.
    // 
    if(g_uiTickIndex < TICK_BUFFER_SIZE)
    {
        g_iTickBuffer[g_uiTickIndex++] = HWREG(NVIC_ST_CURRENT);
    }
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
